<?php

namespace App\Swoole\Server;

use App\Jobs\SocketSendSms;
use App\Jobs\SocketSendSmsExt;
use Illuminate\Container\Container;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Facade;
use Illuminate\Support\Facades\Log;
use Swoole\Coroutine;
use SwooleTW\Http\Coroutine\Context;
use SwooleTW\Http\Server\Facades\Server;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;
use \Throwable;
use SwooleTW\Http\Server\Sandbox;
use SwooleTW\Http\Transformers\Request;
use SwooleTW\Http\Transformers\Response;
use Illuminate\Contracts\Debug\ExceptionHandler;
use Laravel\Lumen\Http\Request as LumenRequest;
use Yurun\Util\Swoole\Guzzle\SwooleHandler;
use Laravel\Lumen\Application as LumenApplication;

/**
 * Class Manager
 */
class Manager extends \SwooleTW\Http\Server\Manager
{

    protected $events = [
        'start',
        'shutDown',
        'workerStart',
        'workerStop',
        'workerExit',
        'packet',
        'bufferFull',
        'bufferEmpty',
        'task',
        'finish',
        'pipeMessage',
        'workerError',
        'managerStart',
        'managerStop',
        'request',
    ];

    public function run()
    {
        //ini_set('memory_limit', '4G');
        parent::run(); // TODO: Change the autogenerated stub
    }

    protected $handleStatic=true;
    protected $publicPath='./public';
    protected $working=false;


    /**
     * Initialize.
     */
    protected function initialize()
    {
        $this->createTables();
        $this->prepareWebsocket();
        $config = $this->container->make('config');
        $this->handleStatic = $config->get('swoole_http.server.handle_static_files', true);
        $this->publicPath = $config->get('swoole_http.server.public_path', base_path('public'));

        if (! $this->container->make(Server::class)->taskworker) {
            $this->setSwooleServerListeners();
        }
    }

    public function onWorkerStop($server,$workerId){
        //关闭数据库连接池+redis连接池
        $config = $this->container->make('config');
        $app = $this->app;
        //\App\Swoole\Coroutine\Context::closeClients($app,$config);
    }

    public function onWorkerStart($server)
    {
        parent::onWorkerStart($server); // TODO: Change the autogenerated stub
        Facade::setFacadeApplication($this->app);
        $this->working = true;
    }

    /**
     * "onRequest" listener.
     *
     * @param \Swoole\Http\Request $swooleRequest
     * @param \Swoole\Http\Response $swooleResponse
     */
    public function onRequest($swooleRequest, $swooleResponse)
    {
        if ($swooleRequest->server['path_info'] == '/favicon.ico' || $swooleRequest->server['request_uri'] == '/favicon.ico') {
            $swooleResponse->end();
            return;
        }
        while (!$this->working){ //worker未初始化完成等待完成后执行
            \Swoole\Coroutine\System::wait(0.005);
        }
        $this->app->make('events')->dispatch('swoole.request');

        $this->resetOnRequest();
        $sandbox = $this->app->make(Sandbox::class);
        try {
            // handle static file request first
            if ($this->handleStatic && Request::handleStatic($swooleRequest, $swooleResponse, $this->publicPath)) {
                return;
            }
            // transform swoole request to illuminate request
            $illuminateRequest = Request::make($swooleRequest)->toIlluminate();

            if (!$sandbox->isLaravel()) { // is lumen app
                $illuminateRequest = LumenRequest::createFromBase($illuminateRequest);
            }
            $illuminateRequest->swooleResponse = $swooleResponse;
            // set current request to sandbox
            $sandbox->setRequest($illuminateRequest);

            Context::setData('_swoole_response', $swooleResponse);

            // enable sandbox
            //$sandbox->enable();
            $this->enableSandbox($sandbox);


            // handle request via laravel/lumen's dispatcher
            $illuminateResponse = $sandbox->run($illuminateRequest);
            $no_chunk = (($illuminateResponse instanceof StreamedResponse && property_exists($illuminateResponse, 'output'))
            || $illuminateResponse instanceof BinaryFileResponse);
            if(!$no_chunk && $illuminateResponse->headers->has('Content-Length')){
                $illuminateResponse->headers->remove('Content-Length');
            }

            // send response
            Response::make($illuminateResponse, $swooleResponse, $swooleRequest)->send();
        } catch (Throwable $e) {
            try {
                Log::error('请求响应错误:'.$illuminateRequest->fullUrl()."\n".$e->getMessage(),$illuminateRequest->all());
                if ($swooleResponse->isWritable()){
                    $exceptionResponse = $this->app
                        ->make(ExceptionHandler::class)
                        ->render(
                            $illuminateRequest,
                            $this->normalizeException($e)
                        );
                    Response::make($exceptionResponse, $swooleResponse, $swooleRequest)->send();
                }
            } catch (Throwable $e) {
                $this->logServerError($e);
            }
        } finally {
            // disable and recycle sandbox resource
            //$sandbox->disable();
            $this->disableSandbox($sandbox);
        }
    }

    protected function enableSandbox($sandbox){
        $app = $sandbox->getApplication();
        $app->instance('app', $app);
        $app->instance(Container::class, $app);
        if ($this->framework === 'lumen') {
            $app->instance(LumenApplication::class, $app);
        }
        //Facade::clearResolvedInstances();
        $clearInstances = array_merge(
            \App\Swoole\Coroutine\Context::$instances,
            \App\Swoole\Coroutine\Context::$instances_more,
            [
                'swoole_response',
                'request'
            ]);

        foreach ($clearInstances as $instance){
            Facade::clearResolvedInstance($instance);
        }
        $sandbox->resetApp($app);

    }

    protected function disableSandbox($sandbox){
        //$env = config('app.env');
        //$memoryUsage = memory_get_usage();
        //$memoryUsageInMB = round($memoryUsage / 1024 / 1024, 8);
        //dump("释放前".Context::getRequestedCoroutineId()."占用内存:{$memoryUsageInMB} MB");
        Context::clear();
        \App\Swoole\Coroutine\Context::clear();
        //Facade::clearFacade();
        gc_collect_cycles();
        gc_mem_caches();
        /*$memoryUsage = memory_get_usage();
        $memoryUsageInMB = round($memoryUsage / 1024 / 1024, 8);
        dump("禁用沙箱".Context::getRequestedCoroutineId()."占用内存:{$memoryUsageInMB} MB");*/

    }


}
